package com.gitee.zycra.future.job.service;

import com.gitee.zycra.future.job.common.bo.CommonResult;
import com.gitee.zycra.future.job.common.bo.JobCalculateBO;
import com.gitee.zycra.future.job.common.enums.ScheduleRatioEnum;
import com.gitee.zycra.future.job.common.model.JobMetaData;
import com.gitee.zycra.future.job.common.model.JobRunning;
import com.gitee.zycra.future.job.common.model.JobSchedule;
import com.gitee.zycra.future.job.common.param.AddScheduleParam;
import com.gitee.zycra.future.job.common.param.UpdateScheduleParam;
import com.gitee.zycra.future.job.common.util.DateUtil;
import com.gitee.zycra.future.job.common.vo.JobScheduleNameVO;
import com.gitee.zycra.future.job.common.vo.JobScheduleVO;
import com.gitee.zycra.future.job.manager.JobDependenceManager;
import com.gitee.zycra.future.job.manager.JobMetaDataManager;
import com.gitee.zycra.future.job.manager.JobRunningManager;
import com.gitee.zycra.future.job.manager.JobScheduleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zycra
 */
@Slf4j
@Service
public class JobScheduleService {

    @Autowired
    private JobMetaDataManager jobMetaDataManager;

    @Autowired
    private JobScheduleManager jobScheduleManager;

    @Autowired
    private JobDependenceManager jobDependenceManager;

    @Autowired
    private JobRunningManager jobRunningManager;

    /**
     * 插入
     *
     * @param addScheduleParam 执行信息对象
     * @return 插入条数
     */
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<String> insertJobSchedule(AddScheduleParam addScheduleParam) {
        JobMetaData jobMetaData = jobMetaDataManager.selectById(addScheduleParam.getJobMetaDataId());
        if (jobMetaData == null) {
            return CommonResult.failed("未找到元数据信息");
        }
        JobCalculateBO jobCalculateBO = JobCalculateBO.buildJobCalculateBO(addScheduleParam, jobMetaData.getJobCycle());
        LocalDateTime normalScheduleTime = jobScheduleManager.calculateScheduleTime(jobCalculateBO);
        JobSchedule jobSchedule = AddScheduleParam.convertToJobSchedule(addScheduleParam, normalScheduleTime);
        jobScheduleManager.insertJobSchedule(jobSchedule);
        return CommonResult.success();
    }

    /**
     * 删除
     *
     * @param id 要删除的ID
     * @return 删除条数
     */
    public CommonResult<String> deleteJobSchedule(Long id) {
        List<Long> childList = jobDependenceManager.selectChildIdByParentId(id);
        if (!CollectionUtils.isEmpty(childList)) {
            return CommonResult.failed("存在子依赖任务");
        }
        jobScheduleManager.deleteJobSchedule(id);
        cancenRunningJob(id);
        return CommonResult.success();
    }

    /**
     * 切换开关
     *
     * @param id     主键
     * @param enable 是否开启
     * @return 切换条数
     */
    public CommonResult<String> toggleEnable(Long id, Boolean enable) {
        jobScheduleManager.toggleEnable(id, enable);
        cancenRunningJob(id);
        if (Boolean.TRUE.equals(enable)) {
            updateScheduleTime(id);
        }
        return CommonResult.success();
    }

    /**
     * 更新
     *
     * @param updateScheduleParam 执行信息对象
     * @return 更新条数
     */
    public CommonResult<String> updateJobSchedule(UpdateScheduleParam updateScheduleParam) {
        jobScheduleManager.updateJobSchedule(UpdateScheduleParam.convertToJobSchedule(updateScheduleParam));
        cancenRunningJob(updateScheduleParam.getId());
        updateScheduleTime(updateScheduleParam.getId());
        return CommonResult.success();
    }

    /**
     * 根据元数据ID查询
     *
     * @param jobMetaDataId 元数据ID
     * @return 查询结果
     */
    public List<JobScheduleVO> queryJobSchedule(Long jobMetaDataId) {
        JobMetaData jobMetaData = jobMetaDataManager.selectById(jobMetaDataId);
        if (jobMetaData == null) {
            return new ArrayList<>();
        }
        List<JobSchedule> list = jobScheduleManager.selectJobScheduleByJobMetaDataId(jobMetaDataId);
        List<JobScheduleVO> result = new ArrayList<>();
        for (JobSchedule jobSchedule : list) {
            result.add(JobScheduleVO.buildJobScheduleVO(jobSchedule, jobMetaData.getJobCycle()));
        }
        return result;
    }

    /**
     * 计算下次执行时间并放入待执行
     */
    public void calculateScheduleTime() {
        LocalDateTime now = DateUtil.getNowLocalDateTime();
        List<JobCalculateBO> calculateBOList = jobScheduleManager.selectJobForCalculate(now.minusYears(1L), now);
        if (CollectionUtils.isEmpty(calculateBOList)) {
            return;
        }
        for (JobCalculateBO jobCalculateBO : calculateBOList) {
            try {
                LocalDateTime normalScheduleTime = jobScheduleManager.calculateScheduleTime(jobCalculateBO);
                LocalDateTime scheduleTime = normalScheduleTime;
                Integer scheduleRatio = jobCalculateBO.getScheduleRatio();
                // 正常轮第一次执行(无执行日志)或正常轮上次执行正常(非执行失败、调度失败)情况或达到最大重试次数，调度时间设为正常下次调度时间
                if (scheduleRatio == null
                        || (!ScheduleRatioEnum.EXECUTE_FAILED.getCode().equals(scheduleRatio)
                        && !ScheduleRatioEnum.TRIGGER_FAILED.getCode().equals(scheduleRatio))
                        || jobCalculateBO.getRetryTimes() == null
                        || jobCalculateBO.getRetryTimes().compareTo(jobCalculateBO.getJobRetryTimes()) >= 0) {
                    jobCalculateBO.setRetryTimes(0);
                    jobScheduleManager.updateJobSchedule(new JobSchedule().setId(jobCalculateBO.getJobScheduleId())
                            .setNextScheduleTime(scheduleTime).setNormalNextTime(normalScheduleTime));
                    continue;
                }
                jobCalculateBO.setRetryTimes(jobCalculateBO.getRetryTimes() + 1);
                scheduleTime = jobCalculateBO.getLastScheduleTime().plusMinutes(jobCalculateBO.getJobRetryInterval());
                jobScheduleManager.updateJobSchedule(new JobSchedule().setId(jobCalculateBO.getJobScheduleId()).setNextScheduleTime(scheduleTime));
            } catch (Exception e) {
                log.error("插入执行信息异常, 等待下次执行, jobCalculateBO={}", jobCalculateBO, e);
            }
        }
    }

    /**
     * 查询所有任务信息
     *
     * @param jobCycle          执行周期
     * @param currentScheduleId 当前调度信息ID
     * @return 所有任务信息
     */
    public List<JobScheduleNameVO> queryAllSchedule(Integer jobCycle, Long currentScheduleId) {
        return jobScheduleManager.selectAllSchedule(jobCycle, currentScheduleId);
    }

    /**
     * 取消正在执行的任务
     *
     * @param jobScheduleId 调度配置ID
     */
    private void cancenRunningJob(Long jobScheduleId) {
        JobRunning jobRunning = jobRunningManager.selectByJobScheduleId(jobScheduleId);
        if (jobRunning != null) {
            log.info("存在执行中的任务，自动取消任务, jobRunning={}", jobRunning);
            jobRunningManager.manualCancel(jobRunning);
        }
    }

    /**
     * 更新下次执行时间
     *
     * @param id 调度配置ID
     */
    private void updateScheduleTime(Long id) {
        JobSchedule jobSchedule = jobScheduleManager.selectById(id);
        if (jobSchedule == null) {
            log.warn("未查询到调度配置, id={}", id);
            return;
        }
        JobMetaData jobMetaData = jobMetaDataManager.selectById(jobSchedule.getJobMetaDataId());
        if (jobMetaData == null) {
            log.warn("未查询到元数据信息, id={}", id);
            return;
        }
        JobCalculateBO jobCalculateBO = JobCalculateBO.buildJobCalculateBO(jobSchedule, jobMetaData.getJobCycle());
        LocalDateTime normalScheduleTime = jobScheduleManager.calculateScheduleTime(jobCalculateBO);
        jobScheduleManager.updateJobSchedule(new JobSchedule().setId(id)
                .setNextScheduleTime(normalScheduleTime).setNormalNextTime(normalScheduleTime));
    }
}
